/*
 * Copyright (C) 2005 Luca Veltri - University of Parma - Italy
 *
 * This file is part of MjSip (http://www.mjsip.org)
 *
 * MjSip is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * MjSip is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with MjSip; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Author(s):
 * Luca Veltri (luca.veltri@unipr.it)
 */

package org.zoolu.sdp;


import java.util.Vector;
import java.util.Enumeration;
//PersonalJava
//import java.util.HashSet;
//import java.util.Iterator;
import org.zoolu.tools.HashSet;
import org.zoolu.tools.Iterator;


/** Class SessionDescriptor handles SIP message bodys formatted according to
 * the Session Description Protocol (SDP).
 * <p> A session description consists of a session-level description
 * (details that apply to the whole session and all media streams) and
 * zero or more media-level descriptions (details that apply onto
 * to a single media stream).
 * <p> The session-level part starts with a
 * `v=' line and continues to the first media-level section.  The media
 * description starts with an `m=' line and continues to the next media
 * description or end of the whole session description.  In general,
 * session-level values are the default for all media unless overridden
 * by an equivalent media-level value.
 * <p> In the current implementation, the session-level description consists
 * of the v, o, s, c, and t SDP fields (lines).
 */
public class SessionDescriptor
{
    /** Version filed. */
    SdpField v;
    /** Origin filed. */
    OriginField o;
    /** Session-rtpmap filed. */
    SessionNameField s;
    /** Connection filed. */
    ConnectionField c;
    /** Time filed. */
    TimeField t;

    /** Vector of session attributes (as Vector of SdpFields). */
    Vector av;

    /** Vector of MediaDescriptors. */
    Vector media;
      
   /*private void init(String owner, String session, String connection, String time)
   {  v=new SdpField('v',"0");
      o=new SdpField('o',owner);
      s=new SdpField('s',session);
      c=new SdpField('c',connection);
      t=new SdpField('t',time);
      media=new HashSet();
   }*/

    /** Inits the SessionDescriptor. */
    private void init(OriginField origin, SessionNameField session, ConnectionField connection, TimeField time)
    {  v=new SdpField('v',"0");
        o=origin;
        s=session;
        c=connection;
        t=time;
        av=new Vector();
        media=new Vector();
    }

    /** Creates a new SessionDescriptor.
     * @param sd the SessionDescriptor clone */
    public SessionDescriptor(SessionDescriptor sd)
    {  init(new OriginField(sd.o),new SessionNameField(sd.s),new ConnectionField(sd.c),new TimeField(sd.t));
        for (int i=0; i<sd.av.size(); i++) av.addElement(new AttributeField((AttributeField)sd.av.elementAt(i)));
        for (int i=0; i<sd.media.size(); i++) media.addElement(new MediaDescriptor((MediaDescriptor)sd.media.elementAt(i)));
    }

    /** Creates a new SessionDescriptor specifing o, s, c, and t fields.
     * @param origin the OriginField
     * @param session the SessionNameField
     * @param connection the ConnectionField
     * @param time the TimeField */
    public SessionDescriptor(OriginField origin, SessionNameField session, ConnectionField connection, TimeField time)
    {  init(origin,session,connection,time);
    }

    /** Creates a new SessionDescriptor specifing o, s, c, and t fields.
     * @param origin the origin value
     * @param session the session value
     * @param connection the connection value
     * @param time the time value */
    public SessionDescriptor(String origin, String session, String connection, String time)
    {  init(new OriginField(origin),new SessionNameField(session),new ConnectionField(connection),new TimeField(time));
    }

    /** Creates a new SessionDescriptor.
     * <p> with:
     * <br>o=<i>owner</i>
     * <br>s=Session SIP/SDP
     * <br>c=IP4 <i>address</i>
     * <br>t=0 0
     * <p> if <i>address</i>==null, '127.0.0.1' is used
     * <br>if <i>owner</i>==null, 'user@'<i>address</i> is used
     * @param owner the owner
     * @param address the IPv4 address */
    public SessionDescriptor(String owner, String address)
    {  if (address==null) address="127.0.0.1";
        if (owner==null) owner="-";
        //String session="Session SIP/SDP";
        String session="-";
        init(new OriginField(owner,"0","0",address),new SessionNameField(session),new ConnectionField("IP4",address),new TimeField());
    }

    /** Creates a default SessionDescriptor.
     * <p> o=user@127.0.0.1
     *     s=Session SIP/SDP
     *     c=127.0.0.1
     *     t=0 0 */
    public SessionDescriptor()
    {  String address="127.0.0.1";
        String owner="user@"+address;
        //String session="Session SIP/SDP";
        String session="-";
        init(new OriginField(owner,"0","0",address),new SessionNameField(session),new ConnectionField("IP4",address),new TimeField());
    }

    /** Creates a new SessionDescriptor from String <i>sdp</i>
     * @param sdp the entire SDP */
    public SessionDescriptor(String sdp)
    {  SdpParser par=new SdpParser(sdp);
        // parse mandatory fields
        v=par.parseSdpField('v');
        if (v==null) v=new SdpField('v',"0");
        o=par.parseOriginField();
        if (o==null) o=new OriginField("unknown");
        s=par.parseSessionNameField();
        if (s==null) s=new SessionNameField("-");
        c=par.parseConnectionField();
        if (c==null) c=new ConnectionField("IP4","0.0.0.0");
        t=par.parseTimeField();
        if (t==null) t=new TimeField();
        while (par.hasMore() && (!par.startsWith("a=") && !par.startsWith("m=")))
        {  // skip unknown lines..
            par.goToNextLine();
        }
        // parse session attributes
        av=new Vector();
        while (par.hasMore() && par.startsWith("a="))
        {  AttributeField attribute=par.parseAttributeField();
            av.addElement(attribute);
        }
        // parse media descriptors
        media=new Vector();
        MediaDescriptor md;
        while ((md=par.parseMediaDescriptor())!=null)
        {  addMediaDescriptor(md);
        }
    }

    /** Sets the origin 'o' field.
     * @param origin the OriginField
     * @return this SessionDescriptor */
    public SessionDescriptor setOrigin(OriginField origin)
    {  o=origin;
        return this;
    }

    /** Gets the origin 'o' field */
    public OriginField getOrigin()
    {  //System.out.println("DEBUG: inside SessionDescriptor.getOwner(): sdp=\n"+toString());
        return o;
    }

    /** Sets the session-rtpmap 's' field.
     * @param session the SessionNameField
     * @return this SessionDescriptor */
    public SessionDescriptor setSessionName(SessionNameField session)
    {  s=session;
        return this;
    }

    /** Gets the session-rtpmap 's' field */
    public SessionNameField getSessionName()
    {  return s;
    }

    /** Sets the connection-information 'c' field.
     * @param connection the ConnectionField
     * @return this SessionDescriptor */
    public SessionDescriptor setConnection(ConnectionField connection)
    {  c=connection;
        return this;
    }

    /** Gets the connection-information 'c' field */
    public ConnectionField getConnection()
    {  return c;
    }

    /** Sets the time 't' field.
     * @param time the TimeField
     * @return this SessionDescriptor */
    public SessionDescriptor setTime(TimeField time)
    {  t=time;
        return this;
    }

    /** Gets the time 't' field */
    public TimeField getTime()
    {  return t;
    }

    /** Adds a new attribute for a particular media
     * @param media the MediaField
     * @param attribute an AttributeField 
     * @return this SessionDescriptor */
    public SessionDescriptor addMedia(MediaField media, AttributeField attribute)
    {  //printlog("DEBUG: media: "+media,5);
        //printlog("DEBUG: attribute: "+attribute,5);
        addMediaDescriptor(new MediaDescriptor(media,null,attribute));
        return this;
    }

    /** Adds a new media.
     * @param media the MediaField
     * @param attributes Vector of AttributeField
     * @return this SessionDescriptor */
    public SessionDescriptor addMedia(MediaField media, Vector attributes)
    {  //printlog("DEBUG: media: "+media,5);
        //printlog("DEBUG: attribute: "+attributes,5);
        addMediaDescriptor(new MediaDescriptor(media,null,attributes));
        return this;
    }

    /** Adds a new MediaDescriptor
     * @param media_desc a MediaDescriptor
     * @return this SessionDescriptor */
    public SessionDescriptor addMediaDescriptor(MediaDescriptor media_desc)
    {  //printlog("DEBUG: media desc: "+media_desc,5);
        media.addElement(media_desc);
        return this;
    }

    /** Adds a Vector of MediaDescriptors
     * @param media_descs Vector if MediaDescriptor 
     * @return this SessionDescriptor */
    public SessionDescriptor addMediaDescriptors(Vector media_descs)
    {  //media.addAll(mediaDescs); // not supported by J2ME..
        for (int i=0; i<media_descs.size(); i++) media.addElement(media_descs.elementAt(i));
        return this;
    }

    /** Gets all MediaDescriptors */
    public Vector<MediaDescriptor> getMediaDescriptors() {
        return media;
    }

    /** Removes all MediaDescriptors */
    public SessionDescriptor removeMediaDescriptor(String media_type)
    {  for (int i=media.size()-1; i>=0; i--)
        if (((MediaDescriptor)media.elementAt(i)).getMedia().getMedia().equals(media_type)) media.removeElementAt(i);
        return this;
    }

    /** Removes all MediaDescriptors */
    public SessionDescriptor removeMediaDescriptors()
    {  //media.clear(); // not supported by J2ME..
        media.setSize(0);
        return this;
    }

    /** Gets the first MediaDescriptor of a particular media.
     * @param media_type the media type
     * @return the MediaDescriptor */
    public MediaDescriptor getMediaDescriptor(String media_type)
    {  for (int i=0; i<media.size(); i++)
    {  MediaDescriptor md=(MediaDescriptor)media.elementAt(i);
        if (md.getMedia().getMedia().equals(media_type)) return md;
    }
        return null;
    }


    /** Keeps only selected media types; other media are removed.
     * @param media_types the media types to be kept
     * @return the MediaDescriptor */
    public SessionDescriptor selectMedia(String[] media_types)
    {  if (media_types!=null)
    {  Vector md_list=new Vector();
        for (int i=0; i<media_types.length; i++)
        {  MediaDescriptor md=getMediaDescriptor(media_types[i]);
            if (md!=null)
            {  removeMediaDescriptor(media_types[i]);
                md_list.addElement(md);
            }
        }
        removeMediaDescriptors();
        addMediaDescriptors(md_list);
    }
        return this;
    }


    /** Adds a Vector of session attributes.
     * @param attributes Vector of AttributeFields
     * @return this SessionDescriptor */
    public SessionDescriptor addAttributes(Vector attributes)
    {  for (int i=0; i<attributes.size(); i++) addAttribute((AttributeField)attributes.elementAt(i));
        return this;
    }

    /** Adds a new attribute
     * @param attribute the new AttributeField
     * @return this MediaDescriptor */
    public SessionDescriptor addAttribute(AttributeField attribute)
    {  av.addElement(new AttributeField(attribute));
        return this;
    }

    /** Removes all session attributes. */
    public SessionDescriptor removeAttributes()
    {  av.setSize(0);
        return this;
    }

    /** Gets a Vector of attribute values.
     * @return a Vector of AttributeField */
    public Vector getAttributes()
    {  Vector v=new Vector(av.size());
        for (int i=0; i<av.size(); i++)
            v.addElement((AttributeField)av.elementAt(i));
        return v;
    }

    /** Whether it has a particular attribute
     * @param attribute_name the attribute rtpmap
     * @return true if found, otherwise returns null */
    public boolean hasAttribute(String attribute_name)
    {  for (int i=0; i<av.size(); i++)
    {  if (((AttributeField)av.elementAt(i)).getAttributeName().equals(attribute_name)) return true;
    }
        return false;
    }

    /** Gets the first AttributeField of a particular attribute rtpmap.
     * @param attribute_name the attribute rtpmap
     * @return the AttributeField, or null if not found */
    public AttributeField getAttribute(String attribute_name)
    {  for (int i=0; i<media.size(); i++)
    {  AttributeField af=(AttributeField)av.elementAt(i);
        if (af.getAttributeName().equals(attribute_name)) return af;
    }
        return null;
    }

    /** Gets a Vector of attribute values of a particular attribute rtpmap.
     * @param attribute_name the attribute rtpmap
     * @return a Vector of AttributeField */
    public Vector getAttributes(String attribute_name)
    {  Vector v=new Vector(av.size());
        for (int i=0; i<av.size(); i++)
        {  AttributeField a=(AttributeField)av.elementAt(i);
            if (a.getAttributeName().equals(attribute_name)) v.addElement(a);
        }
        return v;
    }


    /** Gets a String rapresentation */
    public String toString() {
        StringBuffer sb = new StringBuffer();
        if (v!=null) sb.append(v.toString());
        if (o!=null) sb.append(o.toString());
        if (s!=null) sb.append(s.toString());
        if (c!=null) sb.append(c.toString());
        if (t!=null) sb.append(t.toString());
        for (int i = 0; i < av.size(); i++)
            sb.append(av.elementAt(i).toString());
        for (int i = 0; i < media.size(); i++)
            sb.append(media.elementAt(i).toString());
        return sb.toString();
    }

}
